//      devices.c
//      
//      Copyright 2011 DJ Shaji <djshaji@gmail.com>
//      
//      This program is free software; you can redistribute it and/or modify
//      it under the terms of the GNU General Public License as published by
//      the Free Software Foundation; either version 2 of the License, or
//      (at your option) any later version.
//      
//      This program is distributed in the hope that it will be useful,
//      but WITHOUT ANY WARRANTY; without even the implied warranty of
//      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//      GNU General Public License for more details.
//      
//      You should have received a copy of the GNU General Public License
//      along with this program; if not, write to the Free Software
//      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
//      MA 02110-1301, USA.


#include "devices.h"
#include <sys/stat.h>

int devices_mknod_char ( const char * pathname, int major, int minor )
{
	return mknod ( pathname, S_IFCHR, devices_makedev ( major, minor )) ;
	
}

int devices_mknod_block ( const char * pathname, int major, int minor )
{
	return mknod ( pathname, S_IFBLK, devices_makedev ( major, minor )) ;
}

void devices_mount_core ( void )
{
	message ( "Initializing system core ...", MESSAGE_INFO ) ;
	
	mkdir ( "/proc", 0700 ) ;
	mkdir ( "/sys", 0700 ) ;
	
	mount ( "/proc", "/proc", "proc", 0, NULL ) ;
	mount ( "/sys", "/sys", "sysfs", 0, NULL ) ;
	
	mkdir ( "/dev", 0700 ) ;
	mount ( "/dev", "/dev", "tmpfs", 0, NULL ) ;

	int i = 0 ;
	char tty [20] ;
	
	for ( i = 0 ; i < 64 ; i ++ )
	{
		sprintf ( tty, "/dev/tty%d", i ) ;
		devices_mknod_char ( tty, 4, i ) ;
	
		printf ( "." ) ;
	}

	i = 1 ;
	while ( core_devices [i] != NULL )
	{
		devices_mknod_char ( core_devices [i], 1, i ) ;
		i ++ ;

		printf ( "." ) ;
	}

	mkdir ( "/dev/pts", 0777 ) ;
	mkdir ( "/dev/shm", 0777 ) ;

	char device [20] ;
	i = 0 ;
	
	for ( i = 0 ; i < 8 ; i ++ )
	{
		sprintf ( device, "/dev/loop%d", i ) ;
		devices_mknod_block ( device, 7, i ) ;
	
		printf ( "." ) ;
	}
	
	devices_mknod_block ( "/dev/fd0", 2, 0 ) ;
	
	devices_mknod_char ( "/dev/console", 5, 1 ) ;
	devices_mknod_char ( "/dev/NULL", 1, 3 ) ;
	devices_mknod_char ( "/dev/zero", 1, 5 ) ;
	
	printf ( "\n" ) ;
}

void devices_mount_storage ( void )
{
	message ( "Probing for disks ...", MESSAGE_INFO ) ;

	/** Whole disks **/
	devices_mknod_block ( "/dev/sda", 8, 0 ) ; printf ( "." ) ;
	devices_mknod_block ( "/dev/sdb", 8, 16 ) ; printf ( "." ) ;
	devices_mknod_block ( "/dev/sdc", 8, 32 ) ; printf ( "." ) ;
	devices_mknod_block ( "/dev/sdd", 8, 48 ) ; printf ( "." ) ;
	devices_mknod_block ( "/dev/sde", 8, 56 ) ; printf ( "." ) ;
	devices_mknod_block ( "/dev/sdf", 8, 64 ) ; printf ( "." ) ;
	
	/** Partitions **/
	int i = 0, j = 1, k = 'a' ;
	char dev [16] ;
	
	while ( i < 64 )
	{
		sprintf ( & dev, "/dev/sd%c%d", k, j ) ;
		devices_mknod_block ( dev, 8, i + j ) ;
		
		j ++ ;
		
		if ( j > 10 )
		{
			k ++ ;
			
			i = i + 16 ;
			j = 1 ;
		}
		
		printf ( "." ) ;
	}
	
	/** Generic SCSI access ( cdrom stuff ) **/
	
	for ( i = 0 ; i < 6 ; i ++ )
	{
		sprintf ( & dev, "/dev/sg%d", i ) ;
		devices_mknod_block ( dev, 21, i ) ;

		printf ( "." ) ;
	}
	
	devices_mount_auto () ;
	printf ( "\n" ) ;
}

int devices_mount_device ( char * device, char * location )
{
	int result = -1, i = 0 ;
	while ( filesystems [i] != NULL )
	{
		result = mount ( device, location, filesystems [i], MS_RDONLY, NULL ) ;
		if ( result == 0 )
			break ;
	
		i ++ ;
	}
	
	return result ;
}

int devices_unmount_device ( char * device )
{
	return umount ( device ) ;
}

void devices_mount_auto ( void )
{
	mkdir ( "/mnt", 0777 ) ;
	mkdir ( "/mnt/test", 0777 ) ;
	
	DIR * dir = opendir ( "/dev" ) ;
	struct dirent * ent ;
	
	ent = readdir ( dir ) ;
	while ( ent )
	{
		char * full_name = malloc ( 10 + strlen ( ent -> d_name )) ;
		sprintf ( full_name, "/mnt/%s", ent -> d_name ) ;
		
		char * device = malloc ( 10 + strlen ( ent -> d_name )) ;
		sprintf ( device, "/dev/%s", ent -> d_name ) ;
		
		char * stage2 = malloc ( 30 + strlen ( ent -> d_name )) ;
		sprintf ( stage2, "/mnt/%s/april", ent -> d_name ) ;

		if ( strstr ( ent -> d_name, "sd" ) != NULL )
		{
			int result = -1 ;
			if ( devices_mount_device ( device, "/mnt/test" ) == 0 )
			{
				devices_unmount_device ( "/mnt/test" ) ;
				mkdir ( full_name , 0777 ) ;
				
				result = devices_mount_device ( device, full_name ) ;
				printf ( " [%s] ", ent -> d_name ) ;
			
			}
			
			struct stat st ;
			
			if ( stat ( stage2, & st ) == 0 )
			{
				if ( S_ISDIR ( st.st_mode ))
				{
					remove ( "/usr" ) ;
					symlink ( stage2, "/usr" ) ;
					
					printf ( " < april install found at %s > ", ent -> d_name ) ;
				}
			}
			
			printf ( "." ) ;
		}
		
		free ( device ) ;
		free ( full_name ) ;
		
		ent = readdir ( dir ) ;
		free ( stage2 ) ;
	}

	closedir ( dir ) ;
	remove ( "/mnt/test" ) ;
	
}

	
int devices_mount_device_with_filesystem ( char * device, char * location, char * filesystem )
{
	return mount ( device, location, filesystem, MS_RDONLY, NULL ) ;
}


/****************************************************************/

unsigned int
gnu_dev_major (unsigned long long int dev)
{
	return ((dev >> 8) & 0xfff) | ((unsigned int) (dev >> 32) & ~0xfff);
}

unsigned int
gnu_dev_minor (unsigned long long int dev)
{
	return (dev & 0xff) | ((unsigned int) (dev >> 12) & ~0xff);
}

unsigned long long int
devices_makedev (unsigned int major, unsigned int minor)
{
	return ((minor & 0xff) | ((major & 0xfff) << 8)
		| (((unsigned long long int) (minor & ~0xff)) << 12)
		| (((unsigned long long int) (major & ~0xfff)) << 32));
}

/*************************************************************/


void devices_mknod_alsa ( void )
{
	message ( "Setting up sound system ...", MESSAGE_INFO ) ;
	
	/* OSS */
	int i = 0 ;
	
	while ( oss_devices [i] != NULL )
	{
		char * dev = malloc ( strlen ( oss_devices [i] ) + 20 ) ;
		sprintf ( dev, "%s", oss_devices [i] ) ;
		
		devices_mknod_char ( dev, 14, i ) ;
		free ( dev ) ;
	
		i ++ ;
		
		printf ( "." ) ;
	}
	
	mkdir ( "/dev/snd", 0777 ) ;
	
	/* Now, we make arrangements for only one sound card, while snddevices
	*  make for four. My argument is, few people have cards more than one,
	*  I don't ; notwithstanding, people with RAM < 32M probably don't
	*  either. This program is meant for them, so.
	*/
	
	/* Control ? */
	devices_mknod_char ( "/dev/snd/controlC0", 116, 0 ) ; printf ( "." ) ;
	
	devices_mknod_char ( "/dev/snd/hwC0D0", 116, 4 ) ; printf ( "." ) ;
	devices_mknod_char ( "/dev/snd/hwC0D1", 116, 5 ) ; printf ( "." ) ;
	devices_mknod_char ( "/dev/snd/hwC0D2", 116, 6 ) ; printf ( "." ) ;
	devices_mknod_char ( "/dev/snd/hwC0D3", 116, 7 ) ; printf ( "." ) ;
	
	devices_mknod_char ( "/dev/snd/midiC0D0", 116, 8 ) ; printf ( "." ) ;
	devices_mknod_char ( "/dev/snd/midiC0D1", 116, 9 ) ; printf ( "." ) ;
	devices_mknod_char ( "/dev/snd/midiC0D2", 116, 10 ) ; printf ( "." ) ;
	devices_mknod_char ( "/dev/snd/midiC0D3", 116, 11 ) ; printf ( "." ) ;
	
	/* PCM Wave ? */
	devices_mknod_char ( "/dev/snd/pcmC0D0p", 116, 16 ) ; printf ( "." ) ;
	devices_mknod_char ( "/dev/snd/pcmC0D1p", 116, 17 ) ; printf ( "." ) ;
	devices_mknod_char ( "/dev/snd/pcmC0D2p", 116, 18 ) ; printf ( "." ) ;
	devices_mknod_char ( "/dev/snd/pcmC0D3p", 116, 19 ) ; printf ( "." ) ;
	devices_mknod_char ( "/dev/snd/pcmC0D4p", 116, 20 ) ; printf ( "." ) ;
	devices_mknod_char ( "/dev/snd/pcmC0D5p", 116, 21 ) ; printf ( "." ) ;
	devices_mknod_char ( "/dev/snd/pcmC0D6p", 116, 22 ) ; printf ( "." ) ;
	devices_mknod_char ( "/dev/snd/pcmC0D7p", 116, 23 ) ; printf ( "." ) ;
	
	devices_mknod_char ( "/dev/snd/pcmC0D0c", 116, 24 ) ; printf ( "." ) ;
	devices_mknod_char ( "/dev/snd/pcmC0D1c", 116, 25 ) ; printf ( "." ) ;
	devices_mknod_char ( "/dev/snd/pcmC0D2c", 116, 26 ) ; printf ( "." ) ;
	devices_mknod_char ( "/dev/snd/pcmC0D3c", 116, 27 ) ; printf ( "." ) ;
	devices_mknod_char ( "/dev/snd/pcmC0D4c", 116, 28 ) ; printf ( "." ) ;
	devices_mknod_char ( "/dev/snd/pcmC0D5c", 116, 29 ) ; printf ( "." ) ;
	devices_mknod_char ( "/dev/snd/pcmC0D6c", 116, 30 ) ; printf ( "." ) ;
	devices_mknod_char ( "/dev/snd/pcmC0D7c", 116, 31 ) ; printf ( "." ) ;
	
	devices_mknod_char ( "/dev/snd/aloadC0", 116, 0 ) ; printf ( "." ) ;

	printf ( "\n" ) ;
}
